package cn.xinfei.xdecision.engine.runner.executor.strategy.branch;

import cn.xinfei.xdecision.common.datamodel.DecisionResult;
import cn.xinfei.xdecision.common.model.component.strategyout.StrategyOutput;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.model.enginex.po.EngineVersion;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.utils.util.JsonUtil;
import cn.xinfei.xdecision.common.vo.FieldInfoVo;
import cn.xinfei.xdecision.constant.EngineResultEnum;
import cn.xinfei.xdecision.constant.GlobalConstant;
import cn.xinfei.xdecision.engine.runner.api.DecisionDecisionApi;
import cn.xinfei.xdecision.engine.runner.context.BranchTypeEnum;
import cn.xinfei.xdecision.engine.runner.context.PipelineContext;
import cn.xinfei.xdecision.engine.runner.context.PipelineContextHolder;
import cn.xinfei.xdecision.engine.runner.context.XDecisionContext;
import cn.xinfei.xdecision.engine.runner.executor.NodeHandler;
import cn.xinfei.xdecision.engine.runner.executor.XRunner;
import cn.xinfei.xdecision.engine.runner.executor.model.StateTransmitModel;
import cn.xinfei.xdecision.engine.runner.executor.strategy.EngineNodeRunner;
import cn.xinfei.xdecision.engine.runner.service.DecisionIdBuilder;
import cn.xinfei.xdecision.engine.runner.service.DecisionResultService;
import cn.xinfei.xdecision.engine.runner.vo.EngineApiRequest;
import cn.xinfei.xdecision.model.DecisionApiResponse;
import com.dianping.cat.Cat;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


@XRunner
@Slf4j
@Service
public class ChildEngineNode extends EngineNodeRunner {

    @Autowired
    public DecisionDecisionApi decisionDecisionApi;

    @Autowired
    private DecisionIdBuilder decisionIdBuilder;

    @Autowired
    private DecisionResultService decisionResultService;


    @Override
    public String getServiceName() {
        return EngineNodeRunner.PRE + NodeTypeEnum.CHILD_ENGINE.getType();
    }

    @Override
    public Map<String, FieldInfoVo> getInputFieldInfo(NodeHandler nodeHandler) {

        Map<String, FieldInfoVo> fieldVos = new HashMap<>();

        return fieldVos;
    }

    @Override
    public Map<String, StrategyOutput> getTerminalOutputFieldInfo(NodeHandler nodeHandler) {

        Map<String, StrategyOutput> fieldVos = new HashMap<>();

        return fieldVos;
    }

    /**
     * 子引擎的stack放的是下一个节点
     * @param nodeHandler
     */
//    public void push(NodeHandler nodeHandler)  {
//        EngineNode engineNode = nodeHandler.getEngineNode();
//        String engineCode = engineNode.getEngineCode();
//        //其中一种case，如果子引擎节点后面是end
//        String nextNodeCodes = engineNode.getNextNodeCodes();
//        String nodeCode = engineNode.getNodeCode();
////        PipelineContextHolder.pushNodeCode(engineCode, nodeCode, PipelineContextHolder.getDecisionId());
//        //子节点push的是next node code
//        PipelineContextHolder.pushNodeCode(engineCode, nextNodeCodes, PipelineContextHolder.getDecisionId());
//    }


    @Override
    public StateTransmitModel runNode(NodeHandler nodeHandler) throws Exception {
        XDecisionContext context = XDecisionContext.getContext();
        String parentBranch = PipelineContextHolder.getBranch();
        String requestId = PipelineContextHolder.getRequestId();
        String parentDecisionId = PipelineContextHolder.getDecisionId();
        String scene = PipelineContextHolder.getScene();
        Long newDecisionIdIndex = decisionIdBuilder.getIndex(parentDecisionId);
        String childDecisionId = parentDecisionId + "_" + newDecisionIdIndex;
        EngineNode engineNode = nodeHandler.getEngineNode();
        String oldEngineCode = engineNode.getEngineCode();
        String childEngineCode = engineNode.getNodeJson();
        String nextNodeCodes = engineNode.getNextNodeCodes();
        log.info("执行子引擎节点 runNode childEngineCode={},childDecisionId:{}", childEngineCode, childDecisionId);
        EngineApiRequest engineApiRequest = new EngineApiRequest();
        engineApiRequest.setEngineCode(childEngineCode);
        engineApiRequest.setRequestId(requestId);
        engineApiRequest.setDecisionId(childDecisionId);
        engineApiRequest.setScene(scene);
        //上一次的上下文，作为这次的输入
        engineApiRequest.getIn().putAll(PipelineContextHolder.getContextValue());

        //先深度复制一份父决策流上下文的快照
        PipelineContext parentPipelineContext = PipelineContextHolder.deepCopy();
        String mainDecisionId = PipelineContextHolder.getMainDecisionId();

        //暂存父决策执行结果，如果此处不存，继续执行子决策流，子决策流会插入一部分数据，等子决策流执行成功后，进行update操作时失败了，父决策流的信息就全部丢失了
        updateDecisionResult();

        //-------------------------暂停执行父引擎，开始执行子引擎---------------------------------------------------------------------------------
        //清除掉不从父决策流继承的数据
        this.clearInheritable();
        PipelineContextHolder.setBranch(BranchTypeEnum.CHILD.name());
        PipelineContextHolder.setDecisionId(childDecisionId);
        //设置父决策流decision_id
        PipelineContextHolder.setParentDecisionId(parentDecisionId);
        PipelineContextHolder.setMainDecisionId(mainDecisionId);

        EngineVersion childEngineVersion = metadataProvider.getEngineVersion(childEngineCode);
        PipelineContextHolder.setEngineCode(childEngineCode);
//        PipelineContextHolder.setEngineVersion(engineNode.getEngineVersion());
        PipelineContextHolder.setEngineVersion(childEngineVersion.getVersionNo()+"");
        PipelineContextHolder.setEngineName(childEngineVersion.getEngineName());
        DecisionApiResponse response = decisionDecisionApi.execute(engineApiRequest);

        //-------------------------结束执行子引擎，恢复执行父引擎---------------------------------------------------------------------------------
        StateTransmitModel stateTransmitModel = context.getStateContext();
        log.info("子引擎执行结束,code={},stateTransmitModel={}，nextNodeCodes={},oldDecisionId={}",response.getCode(), stateTransmitModel, nextNodeCodes, parentDecisionId);
        String stateContextCode = stateTransmitModel.getEngineResultEnum().getCode();
        if(stateContextCode.equals(response.getCode())==false){
            log.warn("state inconsistent ");
            Cat.logEvent("dangerous","state inconsistent");
        }
        //为了防止出现状态不一致的情况，用response的终态，覆盖stateContext中的状态
        stateTransmitModel.setEngineResultEnum(EngineResultEnum.getByCode(response.getCode()));
        MDC.put(GlobalConstant.TRACE_ID, requestId);
        MDC.put(GlobalConstant.PID, parentDecisionId);
        MDC.put(GlobalConstant.BRANCH, parentBranch);

        //子决策流的上下文
        PipelineContext childPipelineContext = context.getPipelineContext();
        //父上下文从子上下文继承一部分数据
        this.addInheritable(childPipelineContext, parentPipelineContext);
        PipelineContextHolder.setBranch(parentBranch);
        //重新恢复父决策流上下文
        context.setPipelineContext(parentPipelineContext);

        if (response.getCode().equals(EngineResultEnum.SUCCESS_END.getCode())) {
            //只有pass，才会继续往后走
            //如果子决策流走到了end节点，则需要重置该状态为PROCESSING，继续执行父决策流
            stateTransmitModel.setEngineResultEnum(EngineResultEnum.PROCESSING);
            log.info("重置子决策流状态，继续执行父决策流");
            PipelineContextHolder.popNodeContextModel(nextNodeCodes);
        }

        stateTransmitModel.setNextNodeCode(nextNodeCodes);
        String nodeCode = engineNode.getNodeCode();
        MDC.put(GlobalConstant.NID, nodeCode);
        MDC.put(GlobalConstant.PID, parentDecisionId);
        return stateTransmitModel;
    }

    @Override
    public Map<String, StrategyOutput> getOutputFieldInfo(NodeHandler nodeHandler) {

        Map<String, StrategyOutput> fieldVos = new HashMap<>();

        return fieldVos;
    }
    public void updateDecisionResult( ) throws Exception {
        DecisionResult decisionResult = new DecisionResult();
        try {
            XDecisionContext context = XDecisionContext.getContext();
            StateTransmitModel stateTransmitModel = context.getStateContext();
            String decisionId = PipelineContextHolder.getDecisionId();
            decisionResult.setDecisionId(decisionId);
            decisionResult.setScene(context.getTpCode());
            EngineResultEnum engineResultEnum = stateTransmitModel.getEngineResultEnum();
            decisionResult.setState(engineResultEnum.getState());
            decisionResult.setResult(engineResultEnum.getResult());
            decisionResult.setCode(engineResultEnum.getCode());
            decisionResult.setTpCode(PipelineContextHolder.getTpCode());
            decisionResult.setEngineCode(PipelineContextHolder.getEngineCode());
            decisionResult.setRequestId(PipelineContextHolder.getRequestId());
            decisionResultService.upsertDecisionResultByDecisionId(decisionResult,decisionId,decisionId);
        } catch (Exception e) {
            log.error("updateParentDecisionResultLog error={},decisionResult={}", e.getMessage(), JsonUtil.toJson(decisionResult), e);
            throw  e;
        }

    }



    @Override
    public boolean terminalCondition(NodeHandler nodeHandler, Map<String, Object> nodeOutput, Map<String, Object> nodeInput) {
        return false;
    }
    /**
     * 清除掉那些不继承的数据
     */
    public void clearInheritable() {

        XDecisionContext xDecisionContext = XDecisionContext.getContext();
        PipelineContext pipelineContext = xDecisionContext.getPipelineContext();
        pipelineContext.getCreditNodeStack().clear();
        pipelineContext.getChildNodeNextNodeCodeStack().clear();
//        pipelineContext.getNodeCodeStack().clear();
        pipelineContext.getTrack().clear();
        pipelineContext.getInput().clear();
        pipelineContext.getOutput().clear();
        xDecisionContext.setStateContext(StateTransmitModel.build());
    }

    /**
     * childPipelineContext上下文从parentPipelineContext继承一个新
     *
     * @param parentPipelineContext
     */
    public void addInheritable(PipelineContext parentPipelineContext, PipelineContext childPipelineContext) {

        ConcurrentHashMap<String, Object> context = parentPipelineContext.getContext();
//        Stack<NodeContextModel> nodeCodeStack = parentPipelineContext.getNodeCodeStack();


        childPipelineContext.getContext().putAll(context);
//        childPipelineContext.getBranchRejectInfo().addAll(parentPipelineContext.getBranchRejectInfo());
//        childPipelineContext.getHitResultList().addAll(parentPipelineContext.getHitResultList());
//        childPipelineContext.setNodeCodeStack(nodeCodeStack);

    }
}
